﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SCM.Common;
using SCM.Common.Attributes;
using SCM.Common.Extensions;
using SCM.Model;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;

namespace SCM.WebAPI.Middleware
{
    /// <summary>
    /// Token中间件
    /// </summary>
    public class TokenMiddleware
    {
        private RequestDelegate next;
        private ILogger<TokenMiddleware> logger;

        /// <summary>
        /// 构建中间件
        /// </summary>
        /// <param name="next"></param>
        /// <param name="logger"></param>
        public TokenMiddleware(RequestDelegate next, ILogger<TokenMiddleware> logger)
        {
            this.next = next;
            this.logger = logger;
        }

        /// <summary>
        /// 执行调用
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {

            Endpoint endpoint = context.GetEndpoint();
            if (endpoint == null)
            {
                await next(context);
                return;
            }
            logger.LogInformation($"Ip:[{context.Connection.RemoteIpAddress.MapToIPv4()}],请求：{context.Request.GetDisplayUrl()}");
            EndpointMetadataCollection endpointMetaData = endpoint.Metadata;
            bool hasCustomAuthorizeAttribute = endpointMetaData.Any(x => x is AllowAnonymousAttribute);
            if (hasCustomAuthorizeAttribute)
            {
                await next(context);
                return;
            }
            ViewDataPack<string> viewData = new ViewDataPack<string>();

            SCMAuthorizeAttribute authorize = endpoint.Metadata.FirstOrDefault(p => p is SCMAuthorizeAttribute) as SCMAuthorizeAttribute;
            if (authorize != null)
            {
                string token = context.Request.Headers["Token"].FirstOrDefault();
                if (string.IsNullOrWhiteSpace(token))
                {
                    throw new SCMException("授权失败", HttpStatusCode.Unauthorized, ResultCode.Unauthorized, "");
                }
                AccessToken accessToken = AccessToken.Decry(token);
                if (accessToken == null)
                {
                    throw new SCMException("token解析失败", HttpStatusCode.Unauthorized, ResultCode.Unauthorized, "");
                }
                string value = RedisService.GetRedisHelper().Get(accessToken.Phone);
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new SCMException("token已过期", HttpStatusCode.Unauthorized, ResultCode.Unauthorized, "");
                }
                else
                {
                    RedisLoginUser loginUser = JsonConvert.DeserializeObject<RedisLoginUser>(value);
                    if (CheckRole(authorize.Roles, loginUser))
                    {
                        GenericIdentity identity = new GenericIdentity(loginUser.Phone);
                        identity.AddClaim(new Claim(ClaimTypes.Name, loginUser.UserDisplayName));
                        identity.AddClaim(new Claim("UserId", loginUser.UserID));
                        GenericPrincipal principal = new GenericPrincipal(identity, loginUser.RoleIds);
                        context.User = principal;
                        IConfiguration config = ServiceLocator.Instance.GetRequiredService<IConfiguration>();
                        if (!int.TryParse(config.GetSection("TokenExpire").Value, out int expire))
                        {
                            expire = 600;
                        }
                        RedisService.GetRedisHelper().RefreshTimeOut(accessToken.Phone, expire);
                        await next(context);
                        return;
                    }
                    throw new SCMException("权限不足", HttpStatusCode.OK, ResultCode.Forbidden, "");
                }
            }
            else
            {
                await next(context);
            }
        }

        private static ViewDataPack<string> GetErrorPack(HttpContext context, HttpStatusCode httpStatus, ResultCode resultCode, string error)
        {
            ViewDataPack<string> viewData;
            context.Response.StatusCode = (int)httpStatus;
            context.Response.ContentType = "text/json;charset=utf-8;";
            viewData = new ViewDataPack<string>();
            viewData.Code = resultCode;
            viewData.Message = error;
            viewData.Data = "";
            return viewData;
        }

        private bool CheckRole(string actionRole, RedisLoginUser user)
        {
            if (string.IsNullOrWhiteSpace(actionRole))
            {
                return true;
            }
            else
            {
                return user.RoleIds.Contains(actionRole);
            }
        }
    }
}
